// 
// Copyright (c) 2008, Sergey Parshin (quarck@gmail.com)
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
// * Redistributions of source code must retain the above copyright notice, 
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Sergey Parshin name may not be used to endorse or promote products 
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS I
// NTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//

#ifndef __ITL_SHARED_PTR_H__
#define __ITL_SHARED_PTR_H__

#include <stdexcept>
#include <algorithm>

#include <atomic.h>

namespace itl 
{
	
class invalid_ptr
	: public std::exception
{
};

class null_ptr 
	: public invalid_ptr
{
};

class ref_counter
{
	atomic	m_cnt;
	
	ref_counter(const ref_counter &other);
	ref_counter& operator=(const ref_counter &other);
public:
	inline ref_counter() 
		: m_cnt(1) 
	{
	}
	
	inline ~ref_counter()
	{
		if (m_cnt != 0)
			throw invalid_ptr();
	}
	
	inline ref_counter* retain()
	{
		if (this)
			++ m_cnt; 
		return this;
	}

	// will return true when caller has to delete the object, for hom we are 
	// counting references
	inline bool release()
	{
		bool ret = false;

		if (--m_cnt == 0)
		{
			delete this;
			ret = true;
		}

		return ret;
	}
};

template <typename T>
class shared_ptr
{
	ref_counter 	*cnt;
	T 		*ptr;
public:
	inline shared_ptr()
		: cnt(0)
		, ptr(0)
	{
	}
	
	inline shared_ptr(T *o)
		: cnt(new ref_counter)
		, ptr(o)
	{ 
		
	}
	
	inline shared_ptr(const shared_ptr<T> &other)
		: cnt(other.cnt->retain())
		, ptr(other.ptr)
	{
	}
	
	inline ~shared_ptr()
	{
		if (cnt && cnt->release())
			delete ptr;
		ptr = 0;
		cnt = 0;
	}
	
	inline void reset(T *o2)
	{
		if (cnt && cnt->release())
			delete ptr;
		cnt = new ref_counter;
		ptr = o2;
	}

	inline void reset(const shared_ptr<T> &other)
	{
		if (cnt && cnt->release())
			delete ptr;
		cnt = other.cnt->retain();
		ptr = other.ptr;
	}
	
	void swap(shared_ptr<T> &to)
	{
		std::swap(ptr, to.ptr);
		std::swap(cnt, to.cnt);
	}	
		
	inline shared_ptr<T>& operator=(const shared_ptr<T> &from)
	{
		shared_ptr<T>(from).swap(*this);
		return *this;
	}
	
	inline T* get() const
	{
		return ptr;
	}
	
	inline T& operator*() const
	{
		if (!ptr)
			throw null_ptr();
		return *ptr;
	}
	
	inline T* operator->() const
	{
		return ptr;
	}
	
};
	
template <typename T>
inline bool operator== (const shared_ptr<T> &fst, 
			const shared_ptr<T> &snd)
{
	return fst.get() == snd.get();
}	
	
template <typename T>
inline bool operator!= (const shared_ptr<T> &fst, 
			const shared_ptr<T> &snd)
{
	return fst.get() != snd.get();
}	
	
	
}; // namespace itl

#endif
